//
//  File Name   :   Vector3_f32.cpp
//  Description :   Implementation for the Vector 4 class
//  Author      :   William McVicar
//  Mail        :   write.wil@gmail.com
//

// Precompiled Header
#include "CommonDefines.h"

#ifdef PROTEIN_USE_SIMD

// Library Includes

// Local Includes

// This Include
#include "Vector3_SIMD.h"

namespace Protein
{

// Static Variables
Vector3_f32 Vector3_f32::i	   (  1.0f,  0.0f,  0.0f );
Vector3_f32	Vector3_f32::j	   (  0.0f,  1.0f,  0.0f );
Vector3_f32	Vector3_f32::k	   (  0.0f,  0.0f,  1.0f );
Vector3_f32	Vector3_f32::negi  ( -1.0f,  0.0f,  0.0f );
Vector3_f32	Vector3_f32::negj  (  0.0f, -1.0f,  0.0f );
Vector3_f32	Vector3_f32::negk  (  0.0f,  0.0f, -1.0f );
Vector3_f32	Vector3_f32::negOne( -1.0f, -1.0f, -1.0f );
Vector3_f32	Vector3_f32::One   (  1.0f,  1.0f,  1.0f );
Vector3_f32	Vector3_f32::Zero  (  0.0f,  0.0f,  0.0f );

// Static Function Prototypes

// Implementation

Vector3_f32::Vector3_f32()
{
	m_components = _mm_set_ps( 0.0f, 0.0f, 0.0f, 0.0f );
}

Vector3_f32::Vector3_f32( f32 x, f32 y, f32 z )
{
	m_components = _mm_set_ps( 0.0f, z, y, x );
}

Vector3_f32::Vector3_f32( f32 val )
{
	m_components = _mm_set_ps( 0.0f, val, val, val );
}

Vector3_f32::Vector3_f32( f128 in )
: m_components( in )
{
}

Vector3_f32::Vector3_f32( const Vector3_f32& other )
: m_components( other.m_components )
{
}

Vector3_f32::~Vector3_f32()
{

}

void Vector3_f32::set( f32 x, f32 y, f32 z )
{
  m_components = _mm_set_ps( 0.0f, z, y, x ); 
}

void Vector3_f32::set( f32 val )
{
  m_components = _mm_set_ps( 0.0f, val, val, val );
}

Vector3_f32 Vector3_f32::operator+( const Vector3_f32& rhs ) const
{
	return _mm_add_ps( m_components, rhs.m_components );
}

Vector3_f32 Vector3_f32::operator-( const Vector3_f32& rhs ) const
{
	return _mm_sub_ps( m_components, rhs.m_components );
}

Vector3_f32	Vector3_f32::operator*( f32 rhs ) const
{
	return _mm_mul_ps( m_components, _mm_set1_ps( rhs ) );
}

Vector3_f32	Vector3_f32::operator/( f32 rhs ) const
{
	return _mm_div_ps( m_components, _mm_set1_ps( rhs ) );
}

Vector3_f32 Vector3_f32::operator-() const
{
	return _mm_mul_ps( m_components, negOne.m_components );
}

Vector3_f32& Vector3_f32::operator+=( const Vector3_f32& rhs )
{
	m_components = _mm_add_ps( m_components, rhs.m_components );
	return *this;
}

Vector3_f32& Vector3_f32::operator-=( const Vector3_f32& rhs )
{
	m_components = _mm_sub_ps( m_components, rhs.m_components );
	return *this;
}

Vector3_f32& Vector3_f32::operator*=( f32 rhs )
{
	m_components = _mm_mul_ps( m_components, _mm_set1_ps( rhs ) );
	return *this;
}

Vector3_f32& Vector3_f32::operator/=( f32 rhs )
{
	m_components = _mm_div_ps( m_components, _mm_set1_ps( rhs ) );
	return *this;
}

bool Vector3_f32::operator==( const Vector3_f32& rhs ) const
{
	return ( memcmp( m_components.m128_f32, rhs.m_components.m128_f32, sizeof( f32 ) * 4 ) == 0 );
}

bool Vector3_f32::operator!=( const Vector3_f32& rhs ) const
{
	return ( memcmp( m_components.m128_f32, rhs.m_components.m128_f32, sizeof( f32 ) * 4 ) != 0 );
}

bool Vector3_f32::operator> ( const Vector3_f32& rhs ) const
{
	return ( memcmp( &m_components.m128_f32, rhs.m_components.m128_f32, sizeof( f32 ) * 4 ) > 0 );
}

bool Vector3_f32::operator< ( const Vector3_f32& rhs ) const
{
	return ( memcmp( &m_components.m128_f32, rhs.m_components.m128_f32, sizeof( f32 ) * 4 ) < 0 );
}

bool Vector3_f32::operator>=( const Vector3_f32& rhs ) const
{
	return ( memcmp( &m_components.m128_f32, rhs.m_components.m128_f32, sizeof( f32 ) * 4 ) >= 0 );
}

bool Vector3_f32::operator<=( const Vector3_f32& rhs ) const
{
	return ( memcmp( &m_components.m128_f32, rhs.m_components.m128_f32, sizeof( f32 ) * 4 ) <= 0 );
}

f32 Vector3_f32::dot( const Vector3_f32& in ) const
{
	return _mm_cvtss_f32( _mm_dp_ps( m_components, in.m_components, 0x71 ) );
}

Vector3_f32 Vector3_f32::cross( const Vector3_f32& in ) const
{
	 return _mm_sub_ps( _mm_mul_ps( _mm_shuffle_ps( m_components, m_components, _MM_SHUFFLE(3, 0, 2, 1) ), _mm_shuffle_ps( in.m_components, in.m_components, _MM_SHUFFLE(3, 1, 0, 2) ) ),
					            _mm_mul_ps( _mm_shuffle_ps( m_components, m_components, _MM_SHUFFLE(3, 1, 0, 2) ), _mm_shuffle_ps( in.m_components, in.m_components, _MM_SHUFFLE(3, 0, 2, 1) ) ) );

}

Vector3_f32 Vector3_f32::unitCross( const Vector3_f32& in ) const
{
  return cross( in ).normalised();
}

f32 Vector3_f32::dot( const Vector3_f32& a, const Vector3_f32& b )
{
	return _mm_cvtss_f32( _mm_dp_ps( a.m_components, b.m_components, 0x71 ) );
}

Vector3_f32 Vector3_f32::cross( const Vector3_f32& a, const Vector3_f32& b )
{
	 return _mm_sub_ps( _mm_mul_ps( _mm_shuffle_ps( a.m_components, a.m_components, _MM_SHUFFLE(0, 2, 1, 3) ), _mm_shuffle_ps( b.m_components, b.m_components, _MM_SHUFFLE(1, 0, 2, 3) ) ),
						          _mm_mul_ps( _mm_shuffle_ps( a.m_components, a.m_components, _MM_SHUFFLE(1, 0, 2, 3) ), _mm_shuffle_ps( b.m_components, b.m_components, _MM_SHUFFLE(0, 2, 1, 3) ) ) );

}

Vector3_f32 Vector3_f32::unitCross( const Vector3_f32& a, const Vector3_f32& b )
{
  return cross( a, b ).normalised();
}

f32 Vector3_f32::length() const
{
	return _mm_cvtss_f32( _mm_sqrt_ss( _mm_dp_ps( m_components, m_components, 0x71 ) ) );
}

f32 Vector3_f32::lengthSqr() const
{
	return _mm_cvtss_f32( _mm_dp_ps( m_components, m_components, 0x71 ) );
}

f32 Vector3_f32::length( const Vector3_f32& in )
{
	return _mm_cvtss_f32( _mm_sqrt_ss( _mm_dp_ps( in.m_components, in.m_components, 0x71 ) ) );
}

f32 Vector3_f32::lengthSqr( const Vector3_f32& in )
{
	return _mm_cvtss_f32( _mm_dp_ps( in.m_components, in.m_components, 0x71 ) );
}

void Vector3_f32::normalise()
{
	m_components = _mm_mul_ps( m_components, _mm_rsqrt_ps( _mm_dp_ps( m_components, m_components, 0x7F ) ) );
}

Vector3_f32	Vector3_f32::normalised() const
{
	return _mm_mul_ps( m_components, _mm_rsqrt_ps( _mm_dp_ps( m_components, m_components, 0x7F ) ) );
}

f32 Vector3_f32::distance( const Vector3_f32& in )
{
	f128 temp = _mm_sub_ps( m_components, in.m_components );
	return _mm_cvtss_f32( _mm_sqrt_ps( _mm_dp_ps( temp, temp, 0x71 ) ) );
}

f32 Vector3_f32::distanceSqr( const Vector3_f32& in )
{
	f128 temp = _mm_sub_ps( m_components, in.m_components );
	return _mm_cvtss_f32( _mm_dp_ps( temp, temp, 0x71 ) );
}

f32 Vector3_f32::distance( const Vector3_f32& a, const Vector3_f32& b )
{
	f128 temp = _mm_sub_ps( a.m_components, b.m_components );
	return _mm_cvtss_f32( _mm_sqrt_ps( _mm_dp_ps( temp, temp, 0x71 ) ) );
}

f32 Vector3_f32::distanceSqr( const Vector3_f32& a, const Vector3_f32& b )
{
	f128 temp = _mm_sub_ps( a.m_components, b.m_components );
	return _mm_cvtss_f32( _mm_dp_ps( temp, temp, 0x71 ) );
}

}

#endif